/*
* Author:  Greg Nowak and Vaibhav Sharma
* Michigan State University
* 
* This is the implementation of the NFC Wireless Sensor Network Application
*/
#include "Timer.h"  /* For timer control */
#include "printf.h"
#include "NFCWireless.h"
#include "Msp430Adc12.h"
module NFCWirelessC
{
  	uses interface Timer<TMilli> as Timer0;  /* Reads temperature, logs to flash */
  	uses interface Timer<TMilli> as Timer1;  /* Sends all data in flash */
  	uses interface LocalTime<TMilli>;
 	uses interface Leds;
  	uses interface Boot;
	uses interface LogRead;
	uses interface LogWrite;
	uses interface Read<uint16_t> as TemperatureReading;
	uses interface Read<uint16_t> as BatteryVoltage;
    uses interface Packet;
    uses interface AMPacket;
    uses interface AMSend;
    uses interface Receive;
    uses interface SplitControl as AMControl;
    uses interface HplMsp430GeneralIO as EnergyHarvesterDiscrete;
  provides interface AdcConfigure<const msp430adc12_channel_config_t*> as ACD_A4_Configure;
}
implementation
{
  /***  Module Variables Start ************************************/
  bool mModuleSleeping = FALSE;
  /* Packet Writing Variables */
  uint16_t packetCountWritten = 0;
  uint16_t packetCountRead = 0;
  uint16_t packetSentCount = 0;
  
  
  /* Timer Variables */
  uint32_t mStart_time;
  uint32_t mStop_time;
  uint32_t mTemperatureTimeStamp;
  uint32_t mDesiredSleepSchedule = SHORT_SLEEP_SCHEDULE_MILI; /* Default to short sleep cycle */
  
  /* Data logging variables */
	typedef nx_struct flashLogEntry_t 
	{
    	nx_uint32_t timeStamp;
    	nx_uint16_t temperature;
    	nx_float currentBatteryVoltage;
    	nx_uint8_t nodeID;
    	nx_uint8_t sentStatus;
  	} flashLogEntry_t;

  	bool mLogBusy = FALSE;  	
    flashLogEntry_t mLocalEntry;
    uint16_t MAX_NUM_LOG_ENTRIES = 500;  /* Just for experimentation, not actual!*/
  
  /*  Temperature sensing variables */
  uint16_t mTemperatureReading = 0;
  bool mReadBusy = FALSE;
  
  /*  Packet sending variables */
  flashLogEntry_t mLocalLogEntry;
  bool mSendBusy = FALSE;
  uint32_t mSendPacketsStartTime;
  uint32_t mSendPacketsEndTime;
  bool radioBusy = FALSE;
  message_t pkt;
  void WakeUp();
  void GoToSleep();
  /* Used to print a float via printf */
  void printfFloat(float toBePrinted) {
     uint32_t fi, f0, f1, f2;
     char c;
     float f = toBePrinted;

     if (f<0){
       c = '-'; f = -f;
     } else {
       c = ' ';
     }

     // integer portion.
     fi = (uint32_t) f;

     // decimal portion...get index for up to 3 decimal places.
     f = f - ((float) fi);
     f0 = f*10;   f0 %= 10;
     f1 = f*100;  f1 %= 10;
     f2 = f*1000; f2 %= 10;
     printf("%c%ld.%d%d%d", c, fi, (uint8_t) f0, (uint8_t) f1,  
		(uint8_t) f2);
   }
   
   float mLastVoltageRead;
   task void determineDesiredSleepSchedule()
   {
   
   		/* We are in the largest battery voltage, so we want to set the sleep schedule
   		 * to be the shortest duration (i.e. sleep the least since we have the battery anyway 
   		 */
   		 printf("mLastVoltageRead: ");
   		 printfFloat(mLastVoltageRead);
   		 printf("\nLed0 Threshold: ");
   		 printfFloat(MAX_BATTERY_VOLTAGE - LARGE_SLEEP_SCHEDULE_WIDTH);
   		 printf("\nLed1 Threshold: ");
   		 printfFloat(MAX_BATTERY_VOLTAGE - LARGE_SLEEP_SCHEDULE_WIDTH - SMALL_SLEEP_SCHEDULE_WIDTH);
   		 printf("\nLed2 Threshold: Else\n");
   		 printfflush();
   		 
   		 
   		if( mLastVoltageRead > (MAX_BATTERY_VOLTAGE - LARGE_SLEEP_SCHEDULE_WIDTH))
   		{
   			call Leds.led0On();
   			call Leds.led1Off();
   			call Leds.led2Off();
   			mDesiredSleepSchedule = SHORT_SLEEP_SCHEDULE_MILI;
		}else if( mLastVoltageRead > (MAX_BATTERY_VOLTAGE - LARGE_SLEEP_SCHEDULE_WIDTH - SMALL_SLEEP_SCHEDULE_WIDTH))
		{
   			call Leds.led0Off();
   			call Leds.led1On();
   			call Leds.led2Off();
			mDesiredSleepSchedule = MEDIUM_SLEEP_SCHEDULE_MILI;
		}else{
   			call Leds.led0Off();
   			call Leds.led1Off();
   			call Leds.led2On();
			mDesiredSleepSchedule = LONG_SLEEP_SCHEDULE_MILI;
		}
   	
   }
  	/**
	 * When the ADC Value is done reading, print the result
	 */
	event void BatteryVoltage.readDone(error_t result, uint16_t val){
		
		if ( result == SUCCESS)
		{
			/* Print the value */
			float value = ((float)val * ADC_REFERENCE_VOLTAGE)/MAX_ADC_COUNT;
			mLastVoltageRead = value;
//			printf("ADC Value: ");
//			printfFloat(value);
//			printf(" V\n");
//			printfflush();
			
			
			post determineDesiredSleepSchedule();
		}
	}
	
	/* Configuration necessary to do ADC convertion */
	  const msp430adc12_channel_config_t config = {
	      inch: INPUT_CHANNEL_A3,
	      sref: REFERENCE_VREFplus_AVss,
	      ref2_5v: REFVOLT_LEVEL_1_5,
	      adc12ssel: SHT_SOURCE_ACLK,
	      adc12div: SHT_CLOCK_DIV_1,
	      sht: SAMPLE_HOLD_4_CYCLES,
	      sampcon_ssel: SAMPCON_SOURCE_SMCLK,
	      sampcon_id: SAMPCON_CLOCK_DIV_1
	  };
	  /* Command needed to provide the configuration file */
	  async command const msp430adc12_channel_config_t* ACD_A4_Configure.getConfiguration()
	  {
	    return &config;
	  }
  /*******************************************************/
  /* This is the code that is called when the system starts.*/
  event void Boot.booted()
  {
     mModuleSleeping = TRUE;
	call EnergyHarvesterDiscrete.makeOutput();
    call Timer0.startPeriodic( 500 );
    call Timer1.startPeriodic(mDesiredSleepSchedule);
    //Clear the log on start up for DEBUG only
    if (call LogWrite.erase() != SUCCESS) {
		//printf("Boot.booted(): LogWrite.erase Failed\n");
		//printfflush();
    }
  }
  
    /**
   * To go to sleep, turn off AMControl
   */
  void GoToSleep()
  {
  	mModuleSleeping = TRUE;
  	call EnergyHarvesterDiscrete.clr();
  	call AMControl.stop();
  }
  /**
   * When we wake up, turn on AMControl
   */
  void WakeUp()
  {
  	mModuleSleeping = FALSE;
  	call EnergyHarvesterDiscrete.set();
  	call AMControl.start();	
  }
  /** 
   * The primary purpose of Timer 0 is to kick off a temperature read and log storage
   */
  event void Timer0.fired()
  {
  	if( !mSendBusy )
  	{
  		printf("Timer0 Fired, logging temperature\n");
  
	   	if(!mLogBusy)
	  	{
	  		mLogBusy = TRUE;  		
		  	
			/* Take a temperature reading */
			if( call TemperatureReading.read() != SUCCESS)
			{
				//printf("Timer0.fired(): Reading temperature failed\n");    
				//printfflush();		
			}else{
				//printf("Timer0.fired():Reading temperature succeeded\n");
				//printfflush();
			}        
		}     	
	}
  }
  
  /***************************************************/
  /*  Flash Logger */
	
    event void LogRead.readDone(void* buf, storage_len_t len, error_t err) 
    {
    	//Check to see if the packet read is the right size
        if ( (len == sizeof(flashLogEntry_t)) && (buf == &mLocalLogEntry) )
        {
        	//We've read the log correctly, now send the packet
			//printf("LogRead.readDone:Trying to send a packet\n");
			//printfflush();
			mLogBusy = FALSE;
			//If the radio isn't busy, send all entries in the log
			if( !radioBusy)
	    	{    		
				flashLogEntry_t* lPacket = (flashLogEntry_t*)call Packet.getPayload(&pkt, sizeof (flashLogEntry_t));						    		
				*lPacket = mLocalLogEntry;
				if (call AMSend.send(AM_BROADCAST_ADDR, &pkt, sizeof(flashLogEntry_t)) == SUCCESS) {
				  radioBusy = TRUE;
				  //printf("LogRead.readDone:AMSending succeeded.  Radio is busy\n");
				  //printfflush();
				}else{
					radioBusy = FALSE;
					//printf("LogRead.readDone:AMSend failed\n");
					//printfflush();
				}
				
			}else{
				//printf("LogRead.readDone: Radio is busy\n");
				//printfflush();
			}
    	}else{
    		//printf("LogRead.readDone: buf did not match\n");
    		//printfflush();
		}    	

	}
	
	event void LogWrite.eraseDone(error_t err) 
	{
	}
	event void LogWrite.appendDone(void* buf, storage_len_t len, 
                                 bool recordsLost, error_t err) 
    {
		mLogBusy = FALSE;	
		//printf("LogWrite.appendDone():Logging complete\n");
		//printfflush();
	}

 	event void LogRead.seekDone(error_t err) 
 	{
 	}

  	event void LogWrite.syncDone(error_t err) 
  	{
  	}
	
  /**************************************************/
  
  /***** Sensor Reading  ***********************/
  /**
   * After reading the sensor, we want to store this information to the log
   */
  event void TemperatureReading.readDone(error_t result, uint16_t data) {
  	//printf("Read.readDone(): mLogBusy=%d, packetCount=%d, MAX_NUM_ENTRIES=%d\n", mLogBusy, packetCountWritten, MAX_NUM_LOG_ENTRIES);
  	//printfflush();
  	//We only store a certain amount of data, so once we've reached the limit, stop logging
	if(packetCountWritten < MAX_NUM_LOG_ENTRIES)
	{
	    if (result != SUCCESS)
		{
	  		/* Set the data to garbage because it wasn't right anyway */
	  		data = 0xffff;
		}	  	
	
		mLocalEntry.timeStamp = call Timer0.getNow();
		mLocalEntry.temperature = data;   	
		mLocalEntry.nodeID = TOS_NODE_ID;
		mLocalEntry.sentStatus = 0;
		printf("Node ID: %d,  Temperature: %d\n", TOS_NODE_ID, data);	
		printfflush();
	
		/* Write data to log */
		if (call LogWrite.append(&mLocalEntry, sizeof(flashLogEntry_t)) != SUCCESS) {
			mLogBusy = FALSE;
			//printf("Read.readDone():Packet write failed\n");
			//printfflush();
		}else{
			packetCountWritten++;  //Increase the number of packets written
			//printf("Read.readDone():Packet Written Count: %d\n", packetCountWritten);
			//printfflush();
		}
	}
  }		
 
 
 /****** Sensor Reading End ************************/
 /****** AMC Start *********************************/
  
  event void AMControl.startDone(error_t err) {
  	
    //printf("AMCControl Start Done\n");
    //printfflush();
    if (err == SUCCESS) {
    	//call Timer1.startPeriodic(25000);
    }
    else {
      call AMControl.start();      
    }
  }

  event void AMControl.stopDone(error_t err) {
  }
	
 /**
  * This is in control of sending all the stored log files 
  */
  event void Timer1.fired() {  	
 	static uint32_t Timer1CurrentPeriod = SHORT_SLEEP_SCHEDULE_MILI;
 	/* If the last time we called voltage read prompted us to 
 	 * change the sleep schedule, do so now
 	 */
 	
 	if( Timer1CurrentPeriod != mDesiredSleepSchedule)
 	{
 		call Timer1.startPeriodic(mDesiredSleepSchedule);
 		Timer1CurrentPeriod = mDesiredSleepSchedule;
 	}
	/* Read the battery voltage so the next time we come here we can determine
	 * if we need to change the time of timer1 firing
	 */
	call BatteryVoltage.read();
	/* Turn the radio back on */
    WakeUp();
    
  	if( packetSentCount == 0)
  	{
  		mSendPacketsStartTime = call LocalTime.get();
  	}
	mSendBusy = TRUE;
    if (call LogRead.read(&mLocalLogEntry, sizeof(flashLogEntry_t)) != SUCCESS) 
    {
    	mSendBusy = FALSE;
		// Handle error.
		//printf("Timer1.fired()Load Read Error\n");
		//printfflush();
		
	}else{
		//printf("Timer1.fired() Load Read success\n");
		//printfflush();
	}	
	
  }
  
  event void AMSend.sendDone(message_t* msg, error_t error) {
    if (&pkt == msg && error == SUCCESS) {
      	radioBusy = FALSE;
      	packetSentCount++;
		//printf("AMSend.sendDone(): Packet # %d Sent\n", packetSentCount);
    	//printfflush();
      	
		if( packetSentCount == packetCountWritten)
		{			
			mSendPacketsEndTime = call LocalTime.get();
			printf("AMSend.sendDone(): Time to send %d log entries: %u\n",packetSentCount, (mSendPacketsEndTime - mSendPacketsStartTime));
			printfflush();
			mSendPacketsEndTime = 0;
			mSendPacketsStartTime = 0;
			packetSentCount = 0;
			packetCountWritten = 0;		
			mSendBusy = FALSE;
			GoToSleep();
		}else{
			mLogBusy = FALSE;
			//If there are more packets to send, do another log read to initiate another send
		    if (call LogRead.read(&mLocalLogEntry, sizeof(flashLogEntry_t)) != SUCCESS) 
		    {
				// Handle error.
				//printf("AMSend.sendDone(): Load Read Error\n");
				//printfflush();
			}else{
				//printf("AMSend.sendDone(): Load Read success\n");
				//printfflush();
			}
		}
    }else{
    	
	}

    
  }

  event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
  	      printf("Received Packet\n");
      printfflush();
    if (len == sizeof(flashLogEntry_t)) {
	  flashLogEntry_t* btrpkt = (flashLogEntry_t*)payload;
      printf("Received Packet Timestamp: %d   Temperature: %d\n", btrpkt->timeStamp, btrpkt->temperature);
      printfflush();
	}
	return msg;
  }
}
